﻿using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Reflection;

namespace daStaticAnalyser
{
    using daBase.Analyser;
    using daBase.Objects;
    using daBase.Objects.Metrics;
    using daBase.Objects.AnalysisErrors;
    using daSyntaxAnalyser;
    using daSyntaxAnalyser.SyntaxTree;
    using daSemanticAnalyser;
    using daSemanticAnalyser.SemanticTree;
    using Analysers;

    /// <summary>
    /// Контроллер статического анализа
    /// </summary>
    [LSIAnalyzator()]
    [SLOCAnalyzator()]
    [GilbMetricAnalyzer()]
    [HolstedAnalyzator()]
    [BerlingerAnalyzator()]
    [StilisticaAnalyzator()]
    [GlobalValRefAnalyzer()]
    [ChepinAnalyzer()]
    [SpenAnalyzer()]
    [KafurAnalyzer()]
    [CyclomaticAnalyser()]
    [ShneidevindAnalyzer()]
    [WoodwordAnalyzer()]
    [MAnalyzer()]
    [MacKlurAnalyzer()]
    [PivovarskiAnalyzer()]

    //[BorderValueAnalyzer()]
    //[ChidamberCamererAnalyzator()]

    //[TestAnalyzer()]
    public class StaticAnalyserController : BaseAnalysisController
    {
        
        
        // шаблон "одиночка"
        private static StaticAnalyserController instance;

        /// <summary>
        /// Единственный экземпляр класса контроллера
        /// </summary>
        public static StaticAnalyserController Instance
        {
            get
            {
                if (instance == null)
                    instance = new StaticAnalyserController();
                return instance;
            }
        }

        // анализаторы и ошибки
        private List<Analyser> analysers;

        /// <summary>
        /// Список анализаторов
        /// </summary>
        public List<Analyser> Analysers { get { return analysers; } }

        /// <summary>
        /// Событие "посещен узел дерева"
        /// </summary>
        public event EventHandler<SemanticTreeArgs> SemanticVisitEvent;

        /// <summary>
        /// Событие "изменилась стадия анализа"
        /// </summary>
        public event EventHandler<MessageArgs> StageChangedEvent;

        /// <summary>
        /// Отправка сообщения о новой стадии анализа
        /// </summary>
        /// <param name="Stage">стадия анализа</param>
        private void SetStage(string Stage)
        {
            if (StageChangedEvent != null)
                StageChangedEvent(this, new MessageArgs(Stage));
        }

        /// <summary>
        /// Добавление анализатора в список статических анализаторовы
        /// </summary>
        /// <param name="Analyser"></param>
        private void AddAnalyser(Analyser Analyser)
        {
            analysers.Add(Analyser);
            Analyser.ErrorOccuredEvent += new EventHandler<ErrorArgs>(ErrorOccured);
            Analyser.MetricaCalculatedEvent += new EventHandler<MetricaArgs>(MetricaCalculated);
            if (Analyser is RegularAnalyser)
                SyntaxAnalyserController.Instance.FileAnalysedEvent += new EventHandler<SourceArgs>((Analyser as RegularAnalyser).Analyse);
            else if (Analyser is BaseStaticAnalyser)
            {
                VisitFinish += new EventHandler<EventArgs>((Analyser as BaseStaticAnalyser).FinishAnalize);
                SyntaxTreeVisitor.Instance.VisitEvent += new EventHandler<SyntaxTreeArgs>((Analyser as BaseStaticAnalyser).Analyse);
                this.SemanticVisitEvent += new EventHandler<SemanticTreeArgs>((Analyser as BaseStaticAnalyser).SemanticAnalyse);
            }
        }

        /// <summary>
        /// Конструктор
        /// </summary>
        public StaticAnalyserController()
        {
            SyntaxAnalyserController.Instance.ErrorOccuredEvent += new EventHandler<ErrorArgs>(ErrorOccured);
            SemanticAnalyserController.Instance.ErrorOccuredEvent += new EventHandler<ErrorArgs>(ErrorOccured);
            analysers = new List<Analyser>();
            Attribute[] attributes = Attribute.GetCustomAttributes(this.GetType(), typeof(Analyser));
            foreach (Attribute attribute in attributes)
                AddAnalyser(attribute as Analyser);

            LoadAnalysers();
        }

        public void UpdateAnalysers()
        {
            analysers.Clear();
            Attribute[] attributes = Attribute.GetCustomAttributes(this.GetType(), typeof(Analyser));
            foreach (Attribute attribute in attributes)
                AddAnalyser(attribute as Analyser);

            LoadAnalysers();
        }

        private void LoadAnalysers()
        {
            string path = @"..\..\..\Analizators";//Папка в которой расположены стат. анализаторы
            DirectoryInfo directoryinfo = new DirectoryInfo(path);
            FileInfo[] fileinfo = directoryinfo.GetFiles("*.dll");//Рассматриваем только dll
            foreach (FileInfo thisfileinfo in fileinfo)//Проходимся по всем библиотекам
            {
                Assembly a = Assembly.LoadFrom(thisfileinfo.FullName);//Загружаем сборку
                foreach (Type type in a.GetTypes())//Проходимся по всем типам сборки
                {
                    if (type.IsClass)//Рассматриваем только классы
                    {
                        //object obj = Activator.CreateInstance(type, true);//null;

                        object obj = type.InvokeMember("new", BindingFlags.CreateInstance, null, null, null);//Вот здесь создаётся экземпляр класса по type
                        
                        if (obj is BaseStaticAnalyser)//Если это класс стат.анализатор
                        {
                            AddAnalyser(obj as Analyser);
                        }
                    }
                }
            }
        }

        public event EventHandler<EventArgs> VisitFinish;

        /// <summary>
        /// Статический анализ
        /// </summary>
        /// <param name="filenames">список файлов для анализа</param>
        /// <param name="loadIfNeed">необходимость в подгрузке подключенных модулей, которых нет в списке файлов</param>
        /// <returns>список синтаксических деревьев</returns>
        public List<FileElement> Analyse(List<string> filenames, bool loadIfNeed)
        {
            SetStage("Инициализация анализа");
            foreach (Analyser analyser in analysers)
                analyser.InitAnalysis();
            SetStage("Синтаксический анализ");
            List<FileElement> SyntaxRoots = SyntaxAnalyserController.Instance.Translate(filenames, loadIfNeed);
            SetStage("Семантический анализ");
            List<FileSemanticElement> SemanticRoots = SemanticAnalyserController.Instance.Translate(SyntaxRoots);
            SetStage("Статический анализ");
            if (SemanticVisitEvent != null)
                foreach (FileSemanticElement root in SemanticRoots)
                    SemanticVisitEvent(this, new SemanticTreeArgs(root));
            foreach (FileElement root in SyntaxRoots)
            {
                SyntaxTreeVisitor.Instance.Visit(root);
                VisitFinish(this, new EventArgs());
            }
            return SyntaxRoots;
        }
    }
}
